using System;
using Firefly.Framework.Collections.Spatial;
using Firefly.Framework.Components.Cameras;
using Firefly.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Firefly.Framework.Components.Water
{
    public class QuadWater : Terrain<QuadWaterNode>, IQuadWater
    {
        private bool _firstUpdate;
        private int _patchSize;
        private int _vertexBufferSize;
        private Vector2 _waveMapOffset0;
        private Vector2 _waveMapOffset1;
        private Vector2 _waveMapVelocity0;
        private Vector2 _waveMapVelocity1;
        private RenderTarget2D _refractionMap;
        private RenderTarget2D _reflectionMap;
        private Texture2D _refractionTexture;
        private Texture2D _reflectionTexture;

        public HeightMap HeightMap { get; protected set; }
        public VertexMap VertexMap { get; private set; }
        public RefractionDelegate RefractionDelegate { get; set; }
        public ReflectionDelegate ReflectionDelegate { get; set; }
        public int RenderedIndices
        {
            get
            {
                int count = 0;

                for (int i = 0; i < VertexMap.RenderedIndicies.Length; i++)
                {
                    count += VertexMap.RenderedIndicies[i];
                }

                return count;
            }
        }

        public QuadWater(Game game, int width, int height, int patchSize, int vertexBufferSize, float horizontalScale, float verticalScale, ICamera camera)
            : base(game)
        {
            Shader = new WaterShader(this);
            Camera = camera;
            HeightMap = new HeightMap(width, height);
            _firstUpdate = true;
            LodHeightImpact = 1.0f;
            _patchSize = patchSize;
            HorizontalScale = horizontalScale;
            VerticalScale = verticalScale;
            _vertexBufferSize = vertexBufferSize;
            VertexMap = new VertexMap(HeightMap, vertexBufferSize, verticalScale, horizontalScale);
            int NodeScale = 1 * (patchSize - 1) + 1;
            int NodeLevel = (HeightMap.Height - 1) / (NodeScale - 1);
            MaxNodeDepth = (int)Math.Log(NodeLevel, 2);
            LevelOfDetail = 3.5f;
            RootNode = new QuadWaterNode(HeightMap, _patchSize, _vertexBufferSize, HorizontalScale, 0, 0, 0, null, VertexMap.Verticies);
            this.Add(RootNode);
            RecursiveCreateQuad(RootNode);
            InitializeNodeAdjacency();


            _waveMapVelocity0 = new Vector2(0.001f, 0.003f);
            _waveMapVelocity1 = new Vector2(-0.001f, 0.003f);

            Update(new GameTime());
        }

        private void RecursiveCreateQuad(QuadWaterNode rootNode)
        {
            // Generate left-up child node
            rootNode.LeftUpNode = new QuadWaterNode(HeightMap, _patchSize, _vertexBufferSize, HorizontalScale, rootNode.NodeDepth + 1, rootNode.XPosition, rootNode.YPosition, rootNode, VertexMap.Verticies);
            rootNode.LeftUpNode.Parent = rootNode;
            Add(rootNode.LeftUpNode as QuadWaterNode);

            // Generate left-down child node.
            rootNode.LeftDownNode = new QuadWaterNode(HeightMap, _patchSize, _vertexBufferSize, HorizontalScale, rootNode.NodeDepth + 1, rootNode.XPosition + rootNode.NodeScale / 2, rootNode.YPosition, rootNode, VertexMap.Verticies);
            rootNode.LeftDownNode.Parent = rootNode;
            Add(rootNode.LeftDownNode as QuadWaterNode);

            // Generate right-up child node.
            rootNode.RightUpNode = new QuadWaterNode(HeightMap, _patchSize, _vertexBufferSize, HorizontalScale, rootNode.NodeDepth + 1, rootNode.XPosition, rootNode.YPosition + rootNode.NodeScale / 2, rootNode, VertexMap.Verticies);
            rootNode.RightUpNode.Parent = rootNode;
            Add(rootNode.RightUpNode as QuadWaterNode);

            // Generate right-down child node.
            rootNode.RightDownNode = new QuadWaterNode(HeightMap, _patchSize, _vertexBufferSize, HorizontalScale, rootNode.NodeDepth + 1, rootNode.XPosition + rootNode.NodeScale / 2, rootNode.YPosition + rootNode.NodeScale / 2, rootNode, VertexMap.Verticies);
            rootNode.RightDownNode.Parent = rootNode;
            Add(rootNode.RightDownNode as QuadWaterNode);

            // Recursively go down the tree.
            if (rootNode.NodeDepth < MaxNodeDepth - 1)
            {
                RecursiveCreateQuad(rootNode.LeftUpNode as QuadWaterNode);
                RecursiveCreateQuad(rootNode.LeftDownNode as QuadWaterNode);
                RecursiveCreateQuad(rootNode.RightUpNode as QuadWaterNode);
                RecursiveCreateQuad(rootNode.RightDownNode as QuadWaterNode);
            }
        }

        private void InitializeNodeAdjacency()
        {
            for (int i = 0; i < Count; i++)
            {
                if (this[i].NodeDepth > 0)
                {
                    foreach (QuadWaterNode n in this)
                    {
                        if (n.XPosition == this[i].XPosition + this[i].NodeScale - 1 && n.NodeDepth == this[i].NodeDepth && n.YPosition == this[i].YPosition)
                        {
                            this[i].UpNode = n;
                        }

                        if (n.YPosition == this[i].YPosition + this[i].NodeScale - 1 && n.NodeDepth == this[i].NodeDepth && n.XPosition == this[i].XPosition)
                        {
                            this[i].LeftNode = n;
                        }

                        if (n.XPosition == this[i].XPosition - this[i].NodeScale + 1 && n.NodeDepth == this[i].NodeDepth && n.YPosition == this[i].YPosition)
                        {
                            this[i].DownNode = n;
                        }

                        if (n.YPosition == this[i].YPosition - this[i].NodeScale + 1 && n.NodeDepth == this[i].NodeDepth && n.XPosition == this[i].XPosition)
                        {
                            this[i].RightNode = n;
                        }
                    }
                }
            }
        }

        private void RecursiveChildStatus(QuadWaterNode node)
        {
            node.DistanceFromCamera = (float)(Math.Pow(Math.Abs(Camera.Position.X - ((node.XPosition + (node.NodeScale / 2)) * HorizontalScale)), 2) + Math.Pow(Math.Abs(Camera.Position.Y - ((0 + (node.NodeScale / 2)) * HorizontalScale)), 2) * LodHeightImpact + Math.Pow(Math.Abs(Camera.Position.Z - ((node.YPosition + (node.NodeScale / 2)) * HorizontalScale)), 2));
            node.Status = NodeStatus.LeafNode;

            if (node.NodeDepth < MaxNodeDepth)
            {
                float leftUpLod = node.LeftUpNode.NodeScale * HorizontalScale * LevelOfDetail;
                float leftDownLod = node.LeftDownNode.NodeScale * HorizontalScale * LevelOfDetail;
                float rightUpLod = node.RightUpNode.NodeScale * HorizontalScale * LevelOfDetail;
                float rightDownLod = node.RightDownNode.NodeScale * HorizontalScale * LevelOfDetail;

                leftUpLod *= leftUpLod;
                leftDownLod *= leftDownLod;
                rightUpLod *= rightUpLod;
                rightDownLod *= rightDownLod;

                if (node.DistanceFromCamera < leftUpLod)
                {
                    if (Camera.BoundingFrustum.Intersects(node.LeftUpNode.BoundingBox))
                    {
                        node.Status = NodeStatus.AboveLeaf;
                        RecursiveChildStatus(node.LeftUpNode as QuadWaterNode);
                    }
                    else
                    {
                        node.LeftUpNode.InView = false;
                        node.Status = NodeStatus.AboveLeaf;
                    }
                }

                if (node.DistanceFromCamera < leftDownLod)
                {
                    if (Camera.BoundingFrustum.Intersects(node.LeftDownNode.BoundingBox))
                    {
                        node.Status = NodeStatus.AboveLeaf;
                        RecursiveChildStatus(node.LeftDownNode as QuadWaterNode);
                    }
                    else
                    {
                        node.LeftDownNode.InView = false;
                        node.Status = NodeStatus.AboveLeaf;
                    }
                }

                if (node.DistanceFromCamera < rightUpLod)
                {
                    if (Camera.BoundingFrustum.Intersects(node.RightUpNode.BoundingBox))
                    {
                        node.Status = NodeStatus.AboveLeaf;
                        RecursiveChildStatus(node.RightUpNode as QuadWaterNode);
                    }
                    else
                    {
                        node.RightUpNode.InView = false;
                        node.Status = NodeStatus.AboveLeaf;
                    }
                }

                if (node.DistanceFromCamera < rightDownLod)
                {
                    if (Camera.BoundingFrustum.Intersects(node.RightDownNode.BoundingBox))
                    {
                        node.Status = NodeStatus.AboveLeaf;
                        RecursiveChildStatus(node.RightDownNode as QuadWaterNode);
                    }
                    else
                    {
                        node.RightDownNode.InView = false;
                        node.Status = NodeStatus.AboveLeaf;
                    }
                }
            }
        }

        private void StitchNodes()
        {
            VertexMap.ResetRenderedIndicies();

            foreach (QuadWaterNode node in this)
            {
                if (node.Status == NodeStatus.LeafNode)
                {
                    VertexMap.RenderedIndicies[node.VertexBuffer] += node.NumberOfIndices;

                    if (node.UpNode != null && node.DownNode != null && node.LeftNode != null && node.RightNode != null)
                    {
                        if (node.UpNode.Status < NodeStatus.LeafNode && node.UpNode.InView)
                        {
                            // Northeast node.
                            if (node.LeftNode.Status < NodeStatus.LeafNode && node.LeftNode.InView)
                            {
                                node.StitchedSides = 5;
                            }
                            else
                            {
                                // Northwest node.
                                if (node.RightNode.Status < NodeStatus.LeafNode && node.RightNode.InView)
                                {
                                    node.StitchedSides = 6;
                                }
                                else
                                {
                                    // North node.
                                    node.StitchedSides = 2;
                                }
                            }
                        }
                        else
                        {
                            if (node.DownNode.Status < NodeStatus.LeafNode && node.DownNode.InView)
                            {
                                // Southeast node.
                                if (node.LeftNode.Status < NodeStatus.LeafNode && node.LeftNode.InView)
                                {
                                    node.StitchedSides = 8;
                                }
                                else
                                {
                                    // Southwest node.
                                    if (node.RightNode.Status < NodeStatus.LeafNode && node.RightNode.InView)
                                    {
                                        node.StitchedSides = 7;
                                    }
                                    else
                                    {
                                        // South node.
                                        node.StitchedSides = 4;
                                    }
                                }
                            }
                            else
                            {
                                // East node.
                                if (node.LeftNode.Status < NodeStatus.LeafNode && node.LeftNode.InView)
                                {
                                    node.StitchedSides = 1;
                                }
                                else
                                {
                                    // West node.
                                    if (node.RightNode.Status < NodeStatus.LeafNode && node.RightNode.InView)
                                    {
                                        node.StitchedSides = 3;
                                    }
                                    else
                                    {
                                        // No stitching.
                                        node.StitchedSides = 0;
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        if (node.UpNode == null)
                        {
                            if (node.LeftNode == null)
                            {
                                if (node.DownNode == null)
                                {
                                    if (node.RightNode == null)
                                    {
                                        node.StitchedSides = 0;
                                    }
                                }
                                else
                                {
                                    if (node.RightNode != null)
                                    {
                                        // North node, east corner.
                                        if (node.DownNode.Status < NodeStatus.LeafNode)
                                        {
                                            if (node.RightNode.Status < NodeStatus.LeafNode)
                                            {
                                                // Southwest node.
                                                node.StitchedSides = 7;
                                            }
                                            else
                                            {
                                                // South node.
                                                node.StitchedSides = 4;
                                            }
                                        }
                                        else
                                        {
                                            if (node.RightNode.Status < NodeStatus.LeafNode)
                                            {
                                                // West node.
                                                node.StitchedSides = 3;
                                            }
                                            else
                                            {
                                                // No stitching.
                                                node.StitchedSides = 0;
                                            }
                                        }
                                    }
                                }
                            }
                            else
                            {
                                if (node.RightNode == null)
                                {
                                    // North node, west corner.
                                    if (node.DownNode.Status < NodeStatus.LeafNode)
                                    {
                                        if (node.LeftNode.Status < NodeStatus.LeafNode)
                                        {
                                            // Southeast node.
                                            node.StitchedSides = 8;
                                        }
                                        else
                                        {
                                            // South node.
                                            node.StitchedSides = 4;
                                        }
                                    }
                                    else
                                    {
                                        if (node.LeftNode.Status < NodeStatus.LeafNode)
                                        {
                                            // East node.
                                            node.StitchedSides = 1;
                                        }
                                        else
                                        {
                                            //None
                                            node.StitchedSides = 0;
                                        }
                                    }
                                }
                                else
                                {
                                    // North node, edge.
                                    if (node.DownNode.Status < NodeStatus.LeafNode)
                                    {
                                        if (node.LeftNode.Status < NodeStatus.LeafNode)
                                        {
                                            // Southeast node.
                                            node.StitchedSides = 8;
                                        }
                                        else
                                        {
                                            if (node.RightNode.Status < NodeStatus.LeafNode)
                                            {
                                                // Southwest node.
                                                node.StitchedSides = 7;
                                            }
                                            else
                                            {
                                                // South node.
                                                node.StitchedSides = 4;
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (node.LeftNode.Status < NodeStatus.LeafNode)
                                        {
                                            // East node.
                                            node.StitchedSides = 1;
                                        }
                                        else
                                        {
                                            if (node.RightNode.Status < NodeStatus.LeafNode)
                                            {
                                                // West node.
                                                node.StitchedSides = 3;
                                            }
                                            else
                                            {
                                                // No stitching.
                                                node.StitchedSides = 0;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        else
                        {
                            if (node.DownNode == null)
                            {
                                if (node.LeftNode == null)
                                {
                                    // South node, east corner.
                                    if (node.UpNode.Status < NodeStatus.LeafNode)
                                    {
                                        if (node.RightNode.Status < NodeStatus.LeafNode)
                                        {
                                            // Northwest node.
                                            node.StitchedSides = 6;
                                        }
                                        else
                                        {
                                            // North node.
                                            node.StitchedSides = 2;
                                        }
                                    }
                                    else
                                    {
                                        if (node.RightNode.Status < NodeStatus.LeafNode)
                                        {
                                            // West node.
                                            node.StitchedSides = 3;
                                        }
                                        else
                                        {
                                            // No stitching.
                                            node.StitchedSides = 0;
                                        }
                                    }
                                }
                                else
                                {
                                    if (node.RightNode == null)
                                    {
                                        // South node, west corner.
                                        if (node.UpNode.Status < NodeStatus.LeafNode)
                                        {
                                            if (node.LeftNode.Status < NodeStatus.LeafNode)
                                            {
                                                // Northeast node.
                                                node.StitchedSides = 5;
                                            }
                                            else
                                            {
                                                // North node.
                                                node.StitchedSides = 2;
                                            }
                                        }
                                        else
                                        {
                                            if (node.LeftNode.Status < NodeStatus.LeafNode)
                                            {
                                                // East node.
                                                node.StitchedSides = 1;
                                            }
                                            else
                                            {
                                                //None
                                                node.StitchedSides = 0;
                                            }
                                        }
                                    }
                                    else
                                    {
                                        // South node, map edge.
                                        if (node.UpNode.Status < NodeStatus.LeafNode)
                                        {
                                            if (node.LeftNode.Status < NodeStatus.LeafNode)
                                            {
                                                // Northeast node.
                                                node.StitchedSides = 5;
                                            }
                                            else
                                            {
                                                if (node.RightNode.Status < NodeStatus.LeafNode)
                                                {
                                                    // Northwest node.
                                                    node.StitchedSides = 6;
                                                }
                                                else
                                                {
                                                    // North node.
                                                    node.StitchedSides = 2;
                                                }
                                            }
                                        }
                                        else
                                        {
                                            if (node.LeftNode.Status < NodeStatus.LeafNode)
                                            {
                                                // East node.
                                                node.StitchedSides = 1;
                                            }
                                            else
                                            {
                                                if (node.RightNode.Status < NodeStatus.LeafNode)
                                                {
                                                    // West node.
                                                    node.StitchedSides = 3;
                                                }
                                                else
                                                {
                                                    // No stitching.
                                                    node.StitchedSides = 0;
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                            else
                            {
                                //If not part of north or south edges:
                                if (node.LeftNode == null)
                                {
                                    // East node, map edge.
                                    if (node.RightNode.Status < NodeStatus.LeafNode)
                                    {
                                        if (node.UpNode.Status < NodeStatus.LeafNode)
                                        {
                                            // Northwest node.
                                            node.StitchedSides = 6;
                                        }
                                        else
                                        {
                                            if (node.DownNode.Status < NodeStatus.LeafNode)
                                            {
                                                // Southwest node.
                                                node.StitchedSides = 7;
                                            }
                                            else
                                            {
                                                // West node.
                                                node.StitchedSides = 3;
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (node.UpNode.Status < NodeStatus.LeafNode)
                                        {
                                            // North node.
                                            node.StitchedSides = 2;
                                        }
                                        else
                                        {
                                            if (node.DownNode.Status < NodeStatus.LeafNode)
                                            {
                                                // South node.
                                                node.StitchedSides = 4;
                                            }
                                            else
                                            {
                                                // No stitching.
                                                node.StitchedSides = 0;
                                            }
                                        }
                                    }
                                }
                                else
                                {
                                    //If not east, north or south.
                                    if (node.RightNode == null)
                                    {
                                        // West node, map edge.
                                        if (node.LeftNode.Status < NodeStatus.LeafNode)
                                        {
                                            if (node.UpNode.Status < NodeStatus.LeafNode)
                                            {
                                                // Northeast node.
                                                node.StitchedSides = 5;
                                            }
                                            else
                                            {
                                                if (node.DownNode.Status < NodeStatus.LeafNode)
                                                {
                                                    // Southeast node.
                                                    node.StitchedSides = 8;
                                                }
                                                else
                                                {
                                                    // East node.
                                                    node.StitchedSides = 1;
                                                }
                                            }
                                        }
                                        else
                                        {
                                            if (node.UpNode.Status < NodeStatus.LeafNode)
                                            {
                                                // North node.
                                                node.StitchedSides = 2;
                                            }
                                            else
                                            {
                                                if (node.DownNode.Status < NodeStatus.LeafNode)
                                                {
                                                    // South node.
                                                    node.StitchedSides = 4;
                                                }
                                                else
                                                {
                                                    // No stitching.
                                                    node.StitchedSides = 0;
                                                }
                                            }
                                        }

                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        private void CompileIndexArray()
        {
            VertexMap.ResetRenderedIndicies();
            DistanceSortedNodeList.Clear();

            for (int i = 0; i < this.Count; i++)
            {
                if (this[i].Status == NodeStatus.LeafNode)
                {
                    DistanceSortedNodeList.Add(this[i]);
                }
            }

            DistanceSortedNodeList.Sort(new QuadTreeNodeDistanceComparer<QuadWaterNode>());

            foreach (QuadWaterNode node in DistanceSortedNodeList)
            {
                node.Indices[node.StitchedSides].CopyTo(VertexMap.Indices[node.VertexBuffer], VertexMap.RenderedIndicies[node.VertexBuffer]);
                VertexMap.RenderedIndicies[node.VertexBuffer] += node.NumberOfIndices;
            }
        }
        private void UpdateWaterMaps(GameTime gameTime)
        {
            Color waterColor = new Color(Shader.WaterColor);

            // Clip objects below the water line and render the scene upside down.
            //Engine.Instance.GraphicsDevice.RenderState.CullMode = CullMode.CullClockwiseFace;

            GraphicsService.Instance.GraphicsDevice.SetRenderTarget(0, _reflectionMap);
            GraphicsService.Instance.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, waterColor, 1.0f, 0);

            // Reflection plane in local space.
            Vector4 waterPlaneL = new Vector4(0.0f, -1.0f, 0.0f, 0.0f);

            Matrix inverseWorld = Matrix.Invert(Shader.World);
            inverseWorld = Matrix.Transpose(inverseWorld);

            // Reflection plane in world space.
            Vector4 waterPlaneW = Vector4.Transform(waterPlaneL, inverseWorld);

            Matrix inverseWorldViewProjection = Matrix.Invert(Shader.World * Camera.ViewProjection);
            inverseWorldViewProjection = Matrix.Transpose(inverseWorldViewProjection);

            // Reflection plane in homogeneous space.
            Vector4 waterPlaneH = Vector4.Transform(waterPlaneL, inverseWorldViewProjection);

            GraphicsService.Instance.GraphicsDevice.ClipPlanes[0].IsEnabled = true;
            GraphicsService.Instance.GraphicsDevice.ClipPlanes[0].Plane = new Plane(waterPlaneH);
            Matrix reflectionMatrix = Matrix.CreateReflection(new Plane(waterPlaneW));

            if (ReflectionDelegate != null)
            {
                ReflectionDelegate(gameTime, reflectionMatrix);
            }

            GraphicsService.Instance.GraphicsDevice.RenderState.CullMode = CullMode.CullCounterClockwiseFace;
            GraphicsService.Instance.GraphicsDevice.ClipPlanes[0].IsEnabled = false;
            GraphicsService.Instance.GraphicsDevice.SetRenderTarget(0, null);
            _reflectionTexture = _reflectionMap.GetTexture();

            // If the application is going to send us the refraction map exit early. This is handy if your're already drawing
            // the scene to a render target.
            //if (SuppliedRefractionMap)
            //{
            //    return;
            //}

            // Render the refraction map.
            GraphicsService.Instance.GraphicsDevice.SetRenderTarget(0, _refractionMap);
            GraphicsService.Instance.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, waterColor, 1.0f, 1);

            if (RefractionDelegate != null)
            {
                RefractionDelegate(gameTime);
            }

            GraphicsService.Instance.GraphicsDevice.SetRenderTarget(0, null);
            _refractionTexture = _refractionMap.GetTexture();
        }

        #region IQuadTerrain Members

        public override int Width
        {
            get { return HeightMap.Width; }
        }
        public override int Height
        {
            get { return HeightMap.Height; }
        }

        public override float GetHeight(float x, float z)
        {
            int left = (int)x / (int)HorizontalScale;
            int top = (int)z / (int)HorizontalScale;
            float height;

            if (top > 0 && left > 0 && top < HeightMap.Height - 1 && left < HeightMap.Width - 1)
            {
                float xNormalized = (x % HorizontalScale) / HorizontalScale;
                float zNormalized = (z % HorizontalScale) / HorizontalScale;
                float topHeight = MathHelper.Lerp(
                  HeightMap[left, top],
                  HeightMap[left + 1, top],
                  xNormalized);
                float bottomHeight = MathHelper.Lerp(
                    HeightMap[left, top + 1],
                    HeightMap[left + 1, top + 1],
                    xNormalized);
                height = MathHelper.Lerp(topHeight, bottomHeight, zNormalized);
            }
            else
            {
                height = 0;
            }

            height *= VerticalScale;
            return height;
        }

        public override float GetHeightEx(float x, float z)
        {
            int left = (int)x / (int)HorizontalScale;
            int top = (int)z / (int)HorizontalScale;
            float height;

            if (top > 0 && left > 0 && top < HeightMap.Height - HorizontalScale && left < HeightMap.Width - HorizontalScale)
            {
                float rightDiagDifference = HeightMap[left, top] - HeightMap[left + 1, top + 1];
                float leftDiagDifference = HeightMap[left + 1, top] - HeightMap[left, top + 1];

                if (Math.Abs(rightDiagDifference) >= Math.Abs(leftDiagDifference))
                {
                    if (-((x % HorizontalScale) / HorizontalScale) + 1 > (z % HorizontalScale) / HorizontalScale)
                    {
                        float xNormalized = (x % HorizontalScale) / HorizontalScale;
                        float zNormalized = (z % HorizontalScale) / HorizontalScale;
                        float topHeight = MathHelper.Lerp(
                          HeightMap[left, top],
                          HeightMap[left + 1, top],
                          xNormalized);
                        float bottomHeight = MathHelper.Lerp(
                          HeightMap[left, top + 1],
                          HeightMap[left, top + 1] - (HeightMap[left, top] - HeightMap[left + 1, top]),
                          xNormalized);
                        height = MathHelper.Lerp(topHeight, bottomHeight, zNormalized);
                    }
                    else
                    {
                        float xNormalized = (x % HorizontalScale) / HorizontalScale;
                        float zNormalized = (z % HorizontalScale) / HorizontalScale;
                        float topHeight = MathHelper.Lerp(
                          HeightMap[left, top + 1],
                          HeightMap[left + 1, top + 1],
                          xNormalized);
                        float bottomHeight = MathHelper.Lerp(
                          HeightMap[left + 1, top] + (HeightMap[left, top + 1] - HeightMap[left + 1, top + 1]),
                          HeightMap[left + 1, top],
                          xNormalized);
                        height = MathHelper.Lerp(bottomHeight, topHeight, zNormalized);
                    }
                }
                else
                {
                    if (((x % HorizontalScale) / HorizontalScale) > (z % HorizontalScale) / HorizontalScale)
                    {
                        float xNormalized = (x % HorizontalScale) / HorizontalScale;
                        float zNormalized = (z % HorizontalScale) / HorizontalScale;
                        float topHeight = MathHelper.Lerp(
                          HeightMap[left, top],
                          HeightMap[left + 1, top],
                          xNormalized);
                        float bottomHeight = MathHelper.Lerp(
                          HeightMap[left + 1, top + 1] + (HeightMap[left, top] - HeightMap[left + 1, top]),
                          HeightMap[left + 1, top + 1],
                          xNormalized);
                        height = MathHelper.Lerp(topHeight, bottomHeight, zNormalized);
                    }
                    else
                    {
                        float xNormalized = (x % HorizontalScale) / HorizontalScale;
                        float zNormalized = (z % HorizontalScale) / HorizontalScale;
                        float topHeight = MathHelper.Lerp(
                          HeightMap[left, top + 1],
                          HeightMap[left + 1, top + 1],
                          xNormalized);
                        float bottomHeight = MathHelper.Lerp(
                          HeightMap[left, top],
                          HeightMap[left, top] - (HeightMap[left, top + 1] - HeightMap[left + 1, top + 1]),
                          xNormalized);
                        height = MathHelper.Lerp(bottomHeight, topHeight, zNormalized);
                    }
                }
            }
            else
            {
                height = 0;
            }

            height *= VerticalScale;
            return height;
        }

        public override float GetLodHeight(float x, float z)
        {
            QuadWaterNode currentNode = GetQuadNodeFromPoint(x, z);
            float squareScale = HorizontalScale * currentNode.StepSize;
            int stepSize = currentNode.StepSize;
            int left = ((int)x / (int)squareScale) * stepSize;
            int top = ((int)z / (int)squareScale) * stepSize;
            float height;

            if (top > 0 && left > 0 && top < HeightMap.Height - HorizontalScale && left < HeightMap.Width - HorizontalScale)
            {
                float rightDiagDifference = HeightMap[left, top] - HeightMap[left + stepSize, top + stepSize];
                float leftDiagDifference = HeightMap[left + stepSize, top] - HeightMap[left, top + stepSize];

                if (Math.Abs(rightDiagDifference) >= Math.Abs(leftDiagDifference))
                {
                    if (-((x % squareScale) / squareScale) + 1 > (z % squareScale) / squareScale)
                    {
                        float xNormalized = (x % squareScale) / squareScale;
                        float zNormalized = (z % squareScale) / squareScale;
                        float topHeight = MathHelper.Lerp(
                          HeightMap[left, top],
                          HeightMap[left + stepSize, top],
                          xNormalized);
                        float bottomHeight = MathHelper.Lerp(
                          HeightMap[left, top + stepSize],
                          HeightMap[left, top + stepSize] - (HeightMap[left, top] - HeightMap[left + stepSize, top]),
                          xNormalized);
                        height = MathHelper.Lerp(topHeight, bottomHeight, zNormalized);
                    }
                    else
                    {
                        float xNormalized = (x % squareScale) / squareScale;
                        float zNormalized = (z % squareScale) / squareScale;
                        float topHeight = MathHelper.Lerp(
                          HeightMap[left, top + stepSize],
                          HeightMap[left + stepSize, top + stepSize],
                          xNormalized);
                        float bottomHeight = MathHelper.Lerp(
                          HeightMap[left + stepSize, top] + (HeightMap[left, top + stepSize] - HeightMap[left + stepSize, top + stepSize]),
                          HeightMap[left + stepSize, top],
                          xNormalized);
                        height = MathHelper.Lerp(bottomHeight, topHeight, zNormalized);
                    }
                }
                else
                {
                    if (((x % squareScale) / squareScale) > (z % squareScale) / squareScale)
                    {
                        float xNormalized = (x % squareScale) / squareScale;
                        float zNormalized = (z % squareScale) / squareScale;
                        float topHeight = MathHelper.Lerp(
                          HeightMap[left, top],
                          HeightMap[left + stepSize, top],
                          xNormalized);
                        float bottomHeight = MathHelper.Lerp(
                          HeightMap[left + stepSize, top + stepSize] + (HeightMap[left, top] - HeightMap[left + stepSize, top]),
                          HeightMap[left + stepSize, top + stepSize],
                          xNormalized);
                        height = MathHelper.Lerp(topHeight, bottomHeight, zNormalized);
                    }
                    else
                    {
                        float xNormalized = (x % squareScale) / squareScale;
                        float zNormalized = (z % squareScale) / squareScale;
                        float topHeight = MathHelper.Lerp(
                          HeightMap[left, top + stepSize],
                          HeightMap[left + stepSize, top + stepSize],
                          xNormalized);
                        float bottomHeight = MathHelper.Lerp(
                          HeightMap[left, top],
                          HeightMap[left, top] - (HeightMap[left, top + stepSize] - HeightMap[left + stepSize, top + stepSize]),
                          xNormalized);
                        height = MathHelper.Lerp(bottomHeight, topHeight, zNormalized);
                    }
                }
            }
            else
            {
                height = 0;
            }

            height *= VerticalScale;
            return height;
        }

        public override Vector3 GetNormal(float x, float z)
        {
            //int left = (int)x / (int)HorizontalScale;
            //int top = (int)z / (int)HorizontalScale;
            //Vector3 height;

            //if (top > 0 && left > 0 && top < HeightMap.Height - HorizontalScale && left < HeightMap.Width - HorizontalScale)
            //{
            //    float xNormalized = (x % HorizontalScale) / HorizontalScale;
            //    float zNormalized = (z % HorizontalScale) / HorizontalScale;
            //    Vector3 topNorm;
            //    Vector3.Lerp(
            //      ref NormalMap.NormalStore[left, top],
            //      ref NormalMap.NormalStore[left + 1, top],
            //      xNormalized,
            //      out topNorm);
            //    Vector3 bottomNorm;
            //    Vector3.Lerp(
            //        ref NormalMap.NormalStore[left, top + 1],
            //        ref NormalMap.NormalStore[left + 1, top + 1],
            //        xNormalized,
            //        out bottomNorm);
            //    Vector3.Lerp(ref topNorm, ref bottomNorm, zNormalized, out height);
            //}
            //else
            //{
            //    height = Vector3.Up;
            //}

            //return height;
            return Vector3.Zero;
        }

        public override Vector3 GetNormalEx(float x, float z)
        {
            //int left = (int)x / (int)HorizontalScale;
            //int top = (int)z / (int)HorizontalScale;
            //Vector3 height;

            //if (top > 0 && left > 0 && top < HeightMap.Height - HorizontalScale && left < HeightMap.Width - HorizontalScale)
            //{
            //    float rightDiagDifference = HeightMap[left, top] - HeightMap[left + 1, top + 1];
            //    float leftDiagDifference = HeightMap[left + 1, top] - HeightMap[left, top + 1];

            //    if (Math.Abs(rightDiagDifference) >= Math.Abs(leftDiagDifference))
            //    {
            //        if (-((x % HorizontalScale) / HorizontalScale) + 1 > (z % HorizontalScale) / HorizontalScale)
            //        {
            //            height = Vector3.Cross(NormalMap[left, top] - NormalMap[left + 1, top], NormalMap[left, top] - NormalMap[left, top + 1]);
            //        }
            //        else
            //        {
            //            height = Vector3.Cross(NormalMap[left + 1, top + 1] - NormalMap[left, top + 1], NormalMap[left + 1, top + 1] - NormalMap[left + 1, top]);
            //        }
            //    }
            //    else
            //    {
            //        if (((x % HorizontalScale) / HorizontalScale) > (z % HorizontalScale) / HorizontalScale)
            //        {
            //            height = Vector3.Cross(NormalMap[left + 1, top] - NormalMap[left, top], NormalMap[left + 1, top] - NormalMap[left + 1, top + 1]);
            //        }
            //        else
            //        {
            //            height = Vector3.Cross(NormalMap[left, top + 1] - NormalMap[left + 1, top + 1], NormalMap[left, top + 1] - NormalMap[left, top]);
            //        }
            //    }
            //}
            //else
            //{
            //    height = Vector3.Up;
            //}

            //height *= VerticalScale;
            //return height;
            return Vector3.Zero;
        }

        #endregion

        #region IUpdateable Members

        public override void Update(GameTime gameTime)
        {
            if (Enabled)
            {
                foreach (QuadWaterNode node in this)
                {
                    node.Reset();
                }

                RecursiveChildStatus(RootNode);
                StitchNodes();
                CompileIndexArray();

                if (!_firstUpdate)
                {
                    VertexMap.UpdateIndexBuffers();
                    Shader.View = Camera.View;
                    Shader.Projection = Camera.Projection;

                    float timeDelta = (float)gameTime.ElapsedGameTime.TotalSeconds;
                    _waveMapOffset0 += _waveMapVelocity0 * timeDelta;
                    _waveMapOffset1 += _waveMapVelocity1 * timeDelta;

                    if (_waveMapOffset0.X >= 1.0f || _waveMapOffset0.X <= -1.0f)
                    {
                        _waveMapOffset0.X = 0.0f;
                    }

                    if (_waveMapOffset1.X >= 1.0f || _waveMapOffset1.X <= -1.0f)
                    {
                        _waveMapOffset1.X = 0.0f;
                    }

                    if (_waveMapOffset0.Y >= 1.0f || _waveMapOffset0.Y <= -1.0f)
                    {
                        _waveMapOffset0.Y = 0.0f;
                    }

                    if (_waveMapOffset1.Y >= 1.0f || _waveMapOffset1.Y <= -1.0f)
                    {
                        _waveMapOffset1.Y = 0.0f;
                    }

                    UpdateWaterMaps(gameTime);
                }

                _firstUpdate = false;
            }
        }

        #endregion

        #region IDrawable Members

        public override void Draw(GameTime gameTime)
        {
            if (Visible)
            {
                Shader.ReflectionMap = _reflectionTexture;
                Shader.RefractionMap = _refractionTexture;
                Shader.WaveMapOffset0 = _waveMapOffset0;
                Shader.WaveMapOffset1 = _waveMapOffset1;
                Shader.View = Camera.View;
                Shader.Projection = Camera.Projection;
                Shader.CameraPosition = Camera.Position;
                Shader.Draw(gameTime);
            }
        }

        #endregion

        #region IHasContent Members

        public override void LoadContent()
        {
            Shader.LoadContent();
            //Shader.GlobalScale = HorizontalScale * HeightMap.Height;
            PresentationParameters pp = GraphicsService.Instance.GraphicsDevice.PresentationParameters;
            SurfaceFormat format = pp.BackBufferFormat;
            MultiSampleType msType = pp.MultiSampleType;
            int msQuality = pp.MultiSampleQuality;
            _refractionMap = new RenderTarget2D(GraphicsService.Instance.GraphicsDevice, 512, 512, 1, format, msType, msQuality);
            _reflectionMap = new RenderTarget2D(GraphicsService.Instance.GraphicsDevice, 512, 512, 1, format, msType, msQuality);
        }

        public override void UnloadContent()
        {
            Shader.Dispose();
            VertexMap.Dispose();
        }

        #endregion

        #region IDisposable Members

        protected override void Dispose(bool disposing)
        {
            if (!Disposed)
            {
                if (disposing)
                {
                    UnloadContent();
                }

                Disposed = true;
            }
        }

        ~QuadWater()
        {
            Dispose(false);
        }

        #endregion
    }
}